package org.dj.common.config;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

/**
 * @author dj
 * @version 1.0
 * @date 2024/12/23 14:31
 */
@Data
@Slf4j
@Configuration
@ConfigurationProperties(prefix = "ok.http")
public class OkHttpConfig {
    private Integer connectTimeout; // 连接超时时间，表示在建立连接时的最大等待时间。
    private Integer readTimeout; // 读取超时时间，指在成功建立连接后，从服务器读取数据的最大等待时间。
    private Integer writeTimeout; // 写入超时时间，表示在连接建立后，向服务器发送数据的最大等待时间。
    private Integer maxIdleConnections; // 连接池中最大空闲连接数量，用于优化资源使用。
    private Long keepAliveDuration; // 保持连接的最大持续时间，超过该时间后，空闲连接将被关闭。

    @Bean
    public OkHttpClient okHttpClient() {
        /*
        // 设置连接池，控制同时允许的最大连接数和连接的空闲时间。
        connectionPool(ConnectionPool connectionPool);

        connectTimeout(long timeout, TimeUnit unit);  // 设置连接超时时间。
        readTimeout(long timeout, TimeUnit unit);  // 设置读取超时时间。
        writeTimeout(long timeout, TimeUnit unit);  // 设置写入超时时间。

        retryOnConnectionFailure(boolean enabled);  // 设置是否开启连接失败时的重试。
        addInterceptor(Interceptor interceptor);  // 添加拦截器，用于处理请求重试逻辑。

        // 设置自定义的SSL/TLS Socket工厂和信任管理器。
        sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager);
        // 设置自定义的主机名验证器。
        hostnameVerifier(HostnameVerifier hostnameVerifier);

        proxy(Proxy proxy);  // 设置代理服务器。
        proxyAuthenticator(Authenticator proxyAuthenticator);  // 设置代理服务器的认证器。
         */
        return new OkHttpClient.Builder()
                .sslSocketFactory(sslSocketFactory(), x509TrustManager()) // 使用自定义的 SSL Socket Factory 和信任管理器，以处理 HTTPS 请求。
                .retryOnConnectionFailure(false) // 设置是否在连接失败时重试请求，这里设置为不重试。
                .connectionPool(pool()) // 通过 pool() 方法配置连接池的参数。
                .connectTimeout(connectTimeout, TimeUnit.SECONDS) // 分别设置连接、读取和写入的超时时间
                .readTimeout(readTimeout, TimeUnit.SECONDS)
                .writeTimeout(writeTimeout, TimeUnit.SECONDS)
                .hostnameVerifier((hostname, session) -> true)
                .build();
    }

    @Bean
    public X509TrustManager x509TrustManager() {
        // 用于自定义 SSL 证书的信任策略。在此实现中，所有证书均被信任（注意：这种做法在生产环境中是不安全的）。
        return new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        };
    }

    @Bean
    public SSLSocketFactory sslSocketFactory() {
        // 使用自定义的信任管理器来初始化 SSL 上下文。
        try {
            // 信任任何链接
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            log.error(e.getMessage());
        }
        return null;
    }

    @Bean
    public ConnectionPool pool() {
        // 配置了一个连接池，以管理 HTTP 连接的复用和生命周期。maxIdleConnections 和 keepAliveDuration 用于控制连接的数量和保持时间。
        return new ConnectionPool(maxIdleConnections, keepAliveDuration, TimeUnit.SECONDS);
    }
}
